package optimizers.ganeat.jankenpon;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;

import fitness.EloRecord;
import fitness.EloResult;
import fitness.JankenponResult;

import model.Input;
import model.OligoSystemComplex;
import model.OligoSystemGeneral;
import model.OligoSystemSimple;

import optimizers.commons.RunConfig;
import optimizers.ganeat.Connection;
import optimizers.ganeat.Constants;
import optimizers.ganeat.GA;
import optimizers.ganeat.Individual;
import optimizers.ganeat.Node;
import runconfiguration.TicTacToeConfiguration;
import util.JankenponEventHandler;
import util.MyUtil;
import visualization.PlotExpData;
import visualization.jankenpon.JankenponPlotExpData;
import visualization.jankenpon.JankenponTournamentMatrix;

public class JankenponIndividual extends Individual implements Cloneable, Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public transient ArrayList<Individual> lastMatches = new ArrayList<Individual>();

	public static boolean debug = false;
	

	public JankenponIndividual(Map<String, Integer> innovations,
			Map<String, String> nodeNames, int geneIndex, int indivIndex,
			RunConfig config) {
		super(innovations, nodeNames, geneIndex, indivIndex, config);
		this.fitnessResult = new JankenponResult();//new EloResult();//
		
		
	}
	
	@Override
	protected Node getFirstRedundantNode() {
		for (Node node : nodeGenes) {
			if(!node.name.equals(ConstantsJankenpon.JUDGESTRING) && !node.name.equals(ConstantsJankenpon.JANSTRING) && !node.name.equals(ConstantsJankenpon.KENSTRING) && !node.name.equals(ConstantsJankenpon.PONSTRING) 
					&& !node.name.equals(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.JANSTRING) && !node.name.equals(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.KENSTRING) && 
					!node.name.equals(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.PONSTRING)){
				boolean foundConnectionTo = false;
				for (Connection conn : connectionGenes) {
					if (conn.enabled && conn.to == node) {
						foundConnectionTo = true;
						break;
					}
				}
				if (!foundConnectionTo) {
					return node;
				}
			}
		}
		return null;
	}

	public Individual createMatch(Individual contestant){
		
		if(contestant == null){
			System.err.println("May be a cause of problems");
			return this;
		}
		
		
			Individual match = this.clone();
			
			if(this.nodeGenes.size() < 7 || contestant.nodeGenes.size() < 7){
				System.err.println("Size error");
			}
			// more sanity check
			for(Node node : this.nodeGenes){
				if(this.getConnection(node.name,ConstantsJankenpon.JUDGESTRING)!= null){
					return null; // What the hell? immediate loss
				}
			}
			for(Connection connect : this.connectionGenes){
				if(connect.to.name.startsWith(ConstantsJankenpon.contestantPrefix)){
					System.err.println("Bad manners");
					return null; // immediate loss as well
				}
			}
			for(Node node : contestant.nodeGenes){
				if(node.name != ConstantsJankenpon.JUDGESTRING){
					if(contestant.getConnection(node.name,ConstantsJankenpon.JUDGESTRING)!= null){
						return this; // What the hell? immediate loss
					}
					if(node.name.startsWith(ConstantsJankenpon.contestantPrefix)){ //he is talking about us
						if(match.getNode(node.name.substring(1)) == null) //if we don't know what he is talking about, then fine, let's have 'im talking
							match.nodeGenes.add(new Node(node.name.substring(1),node.parameter,node.concentration,node.type)); //change the name to match
					} else if (node.type == Node.INHIBITING_SEQUENCE && node.name.substring(1).startsWith(ConstantsJankenpon.contestantPrefix)){ // Also a way to talk about us
						Node from = contestant.getInhibitedConnection(node).from;
						Node to = contestant.getInhibitedConnection(node).to;
						if(match.getNode("I"+from.name.substring(1)+(to.name.startsWith(ConstantsJankenpon.contestantPrefix)?to.name.substring(1):ConstantsJankenpon.contestantPrefix+to.name))==null){
							match.nodeGenes.add(new Node("I"+from.name.substring(1)+(to.name.startsWith(ConstantsJankenpon.contestantPrefix)?to.name.substring(1):ConstantsJankenpon.contestantPrefix+to.name),node.parameter,node.concentration,node.type));
						}
					} else { //his stuff
						if(node.type == Node.INHIBITING_SEQUENCE){
							Node from = contestant.getInhibitedConnection(node).from;
							Node to = contestant.getInhibitedConnection(node).to;
							String fromname = from.name.equals(ConstantsJankenpon.JUDGESTRING) ? ConstantsJankenpon.JUDGESTRING : ConstantsJankenpon.contestantPrefix+from.name;
							match.nodeGenes.add(new Node("I"+fromname+ConstantsJankenpon.contestantPrefix+to.name,node.parameter,node.concentration,node.type));
						} else if(match.getNode(ConstantsJankenpon.contestantPrefix+node.name) == null){ //if we don't know what he is talking about, then fine, let's have 'im talking
							match.nodeGenes.add(new Node(ConstantsJankenpon.contestantPrefix+node.name,node.parameter,node.concentration,node.type)); //change the name to match
						} else { //we HAVE to use his parameters
							Node local = match.getNode(ConstantsJankenpon.contestantPrefix+node.name); // it modifies us... Is that fine?
							
							local.concentration = node.concentration;
							local.parameter = node.parameter;
							local.type = node.type;
								
						}
					}
				}
			}
			
			//Now, add the connections
			Node from, to;
			for(Connection connect : contestant.connectionGenes){
				if(connect.enabled){ // we don't care about the rest
					if(connect.from.name == ConstantsJankenpon.JUDGESTRING){
						from = match.getNode(ConstantsJankenpon.JUDGESTRING);
					} else {
						from = connect.from.name.startsWith(ConstantsJankenpon.contestantPrefix) ? match.getNode(connect.from.name.substring(1)) : match.getNode(ConstantsJankenpon.contestantPrefix+connect.from.name);
					}
					if(connect.to.type == Node.INHIBITING_SEQUENCE){
						String debugconnecttoname = connect.to.name;
						String debugthething = "I"+ConstantsJankenpon.contestantPrefix+contestant.getInhibitedConnection(connect.to).from.name+(contestant.getInhibitedConnection(connect.to).to.name.startsWith(ConstantsJankenpon.contestantPrefix)?contestant.getInhibitedConnection(connect.to).to.name.substring(1):ConstantsJankenpon.contestantPrefix+contestant.getInhibitedConnection(connect.to).to.name);
						String firstPart = contestant.getInhibitedConnection(connect.to).from.name;
						if(!firstPart.equals("A"))
							firstPart = firstPart.startsWith(ConstantsJankenpon.contestantPrefix)? firstPart.substring(1):ConstantsJankenpon.contestantPrefix+firstPart;
						String secondPart = contestant.getInhibitedConnection(connect.to).to.name;
						secondPart = secondPart.startsWith(ConstantsJankenpon.contestantPrefix)? secondPart.substring(1):ConstantsJankenpon.contestantPrefix+secondPart;
						
						
					to = match.getNode("I"+firstPart+secondPart);
							} else {
					to = connect.to.name.startsWith(ConstantsJankenpon.contestantPrefix) ? match.getNode(connect.to.name.substring(1)) : match.getNode(ConstantsJankenpon.contestantPrefix+connect.to.name);
					}
					//First, check integrity. In particular, cannot corrupt the JUDGE
					if(to == null){
						
						System.err.println("Node reference not found: "+connect.to.name+" from the individual "+connect);
					}
					if((!to.name.startsWith(ConstantsJankenpon.contestantPrefix) && to.type == Node.SIMPLE_SEQUENCE)){
						//Hoooooooo no, you ain't going there
						System.err.println("Integrity failure");
						//I should throw something
						return this; //I win by default
					}
					if(match.getConnection(from.name, to.name)==null){
						match.addConnection(from, to, true, connect.parameter);
					} else { // Err, that means "I" am playing toward him
						System.err.println("Internal integrity failure");
						return null;
					}
				}
			}
			return match;
	}
	
	protected JankenponGame competeAgainst(Individual contestant){
		double score = 0.0;
		JankenponGame game = new JankenponGame();
		
		
		if(this.lastMatches != null){
		this.lastMatches.add(contestant);
		((JankenponIndividual) contestant).lastMatches.add(this);
		}
		if(debug ){
			game.score = 55.0;
			game.iplayed = new int[]{1,1,1,1,1,1,1,1,1,1};
			game.uplayed = new int[]{3,3,3,3,-1,-1,-1,-1,-1,-1};
			return game;
		}
		
			Individual match = createMatch(contestant);
			if(match == null){
				game.score = 0;
				return game;
			} else if (match.equals(this)){ // The match is MINE
				game.score = ConstantsJankenpon.invalidPlay;
				return game;
			}
			// Now ready to rumble!
			OligoSystemGeneral g = match.getOligoModel();
			OligoSystemComplex oligo = new OligoSystemComplex(g.nSimpleSequences, g.templates, g.seqK, g.inhK, g.seqConcentration, null);
			oligo.inputs = new Input[ConstantsJankenpon.rounds];
			for(int i= 0; i< ConstantsJankenpon.rounds; i++){
				oligo.inputs[i] = new Input(g.nameToInt.get(ConstantsJankenpon.JUDGESTRING),ConstantsJankenpon.pulse,ConstantsJankenpon.initialTime+i*ConstantsJankenpon.fightTime);
			}
			
			double[][] fight = oligo.calculateTimeSeries(new JankenponEventHandler(oligo.inputs));
			int time = ConstantsJankenpon.initialTime;
			for(int i=0;i<ConstantsJankenpon.rounds;i++){
			//Round i
				time += ConstantsJankenpon.fightTime;
			double jan = fight[g.nameToInt.get(ConstantsJankenpon.JANSTRING)][time-1];
			double ken = fight[g.nameToInt.get(ConstantsJankenpon.KENSTRING)][time-1];
			double pon = fight[g.nameToInt.get(ConstantsJankenpon.PONSTRING)][time-1];
			int iplayed = -1;
			if(jan > ken + ConstantsJankenpon.playThreshold && jan > pon + ConstantsJankenpon.playThreshold){
				iplayed = ConstantsJankenpon.JAN;
			} else if (ken > jan + ConstantsJankenpon.playThreshold && ken > pon + ConstantsJankenpon.playThreshold){
				iplayed = ConstantsJankenpon.KEN;
			} else if (pon > jan + ConstantsJankenpon.playThreshold && pon > ken + ConstantsJankenpon.playThreshold){
				iplayed = ConstantsJankenpon.PON;
			} 
			jan = fight[g.nameToInt.get(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.JANSTRING)][time-1];
			ken = fight[g.nameToInt.get(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.KENSTRING)][time-1];
			pon = fight[g.nameToInt.get(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.PONSTRING)][time-1];
			int opponentplayed = -1;
			if(jan > ken + ConstantsJankenpon.playThreshold && jan > pon + ConstantsJankenpon.playThreshold){
				opponentplayed = ConstantsJankenpon.JAN;
			} else if (ken > jan + ConstantsJankenpon.playThreshold && ken > pon + ConstantsJankenpon.playThreshold){
				opponentplayed = ConstantsJankenpon.KEN;
			} else if (pon > jan + ConstantsJankenpon.playThreshold && pon > ken + ConstantsJankenpon.playThreshold){
				opponentplayed = ConstantsJankenpon.PON;
			} 
			game.iplayed[i] = iplayed;
			game.uplayed[i] = opponentplayed;
			if((iplayed == ConstantsJankenpon.JAN && opponentplayed == ConstantsJankenpon.PON) || (iplayed == ConstantsJankenpon.KEN && opponentplayed == ConstantsJankenpon.JAN) || (iplayed == ConstantsJankenpon.PON && opponentplayed == ConstantsJankenpon.KEN)){
				score++;
			} else if(iplayed == opponentplayed){
				score += 0.5; // so that one victory and all the rest ex-equo gives victory
			} else if (opponentplayed == -1){
				score++;
			}
			if(iplayed == -1 || opponentplayed == -1){
				//System.out.println("debug this");
			}
			}
		game.score = score;
		return game;
	}
	
	@Override
	public String mutateAddActivation() {
		ArrayList<Connection> possibleActivations = new ArrayList<Connection>();
		for (Node from : nodeGenes) {
			if (from.type != Node.INHIBITING_SEQUENCE) {
				for (Node to : nodeGenes) {
					if (!to.name.equals("i1") && !to.name.equals("i2") && !to.name.startsWith(ConstantsJankenpon.contestantPrefix)
							&& !to.name.equals(ConstantsJankenpon.JUDGESTRING)	&& getConnection(from.name, to.name) == null) {
						possibleActivations.add(new Connection(0, from, to,
								true, 0));
					}
				}
			}
		}
		if (possibleActivations.size() == 0) {
			mutationInfo = "Cannot add activation";
		} else {
			int index = rand.nextInt(possibleActivations.size());
			Connection connection = possibleActivations.get(index);
			addConnection(connection.from, connection.to, true,
					config.template.defaultValue);
			mutationInfo = "Mutate Add Activation: " + connection.from.name
					+ "->" + connection.to.name;
		}
		return mutationInfo;
	}
	
	@Override
	public String mutateAddInhibition(Node nodeFrom, Node nodeTo,
			double parameter) {
		if(nodeTo.name.startsWith(ConstantsJankenpon.contestantPrefix) || nodeTo.name.equals(ConstantsJankenpon.JUDGESTRING)){
			return "Cannot add inhibition";
		}
		ArrayList<Connection> possibleConnections = new ArrayList<Connection>();
		for (Connection conn : connectionGenes) {
			if (conn.to == nodeTo && conn.enabled) {
				possibleConnections.add(conn);
			}
		}
		Connection conn = null;
		if (possibleConnections.size() == 0) {
			conn = addConnection(nodeTo, nodeTo, true,
					config.template.defaultValue);
		} else {
			conn = possibleConnections.get(rand.nextInt(possibleConnections
					.size()));
		}
		Node inhibitionNode = new Node("I" + conn.from.name + conn.to.name,
				config.inhK.defaultValue, config.seqC.defaultValue,
				Node.INHIBITING_SEQUENCE);
		if (getNode(inhibitionNode.name) == null) {
			nodeGenes.add(inhibitionNode);
		} else {
			inhibitionNode = getNode(inhibitionNode.name);
		}
		addConnection(nodeFrom, inhibitionNode, true, parameter);
		mutationInfo = "Mutate Add Inhibition: " + nodeFrom.name + "->"
				+ inhibitionNode.name;

		return mutationInfo;

	}
	
	public JankenponIndividual clone(){
		JankenponIndividual clo = null;
		try {
			clo = (JankenponIndividual) super.clone();
			clo.connectionGenes = (ArrayList<Connection>) this.connectionGenes.clone();
			clo.nodeGenes = (ArrayList<Node>) this.nodeGenes.clone();
		
			
			
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return clo;
	}
	
	/**
	 * We display the finals, instead...
	 */
	@Override
	public JPanel displayTimeSereis() {
		Individual match;
		if(this.lastMatches != null && !this.lastMatches.isEmpty()){
		int index = rand.nextInt(this.lastMatches.size());
		for(EloRecord res : ((JankenponResult)this.fitnessResult).results){
			if(res.isDynamic()){
				index = ((JankenponResult)this.fitnessResult).results.indexOf(res);
				break;
			}
		}
		
			match = createMatch(this.lastMatches.get(index));
		} else{
			match = this.clone();
		}
		OligoSystemGeneral oligoModel = match.getOligoModel();
		OligoSystemSimple oligo = new OligoSystemSimple(oligoModel.nSimpleSequences, oligoModel.templates, oligoModel.seqK, oligoModel.inhK, oligoModel.seqConcentration, oligoModel.exo.activity);
		
		oligo.inputs = new Input[ConstantsJankenpon.rounds];
		for(int i= 0; i< ConstantsJankenpon.rounds; i++){
			oligo.inputs[i] = new Input(oligoModel.nameToInt.get(ConstantsJankenpon.JUDGESTRING),ConstantsJankenpon.pulse,ConstantsJankenpon.initialTime+i*ConstantsJankenpon.fightTime);
		}
		
		double[][] data = oligo.calculateTimeSeries(new JankenponEventHandler(oligo.inputs));
		String[] seqNames = new String[data.length];
		for (int i = 0; i < data.length; i++) {
			seqNames[i] = oligoModel.intToName.get(i);
			if (seqNames[i] == null)
				seqNames[i] = String.valueOf(i);
		}
		final JankenponPlotExpData demo = new JankenponPlotExpData("Generation "
				+ this.generationIndex + " Index " + this.individualIndex,
				data, seqNames, "Time", "Expression");
		return (demo);
	}
	
	@Override
	public JPanel printModelInTextArea() {
		JPanel info = new JPanel();
		info.add(new JankenponTournamentMatrix((JankenponResult)this.fitnessResult));
		JTabbedPane tabbedPane = new JTabbedPane();
		info.add(tabbedPane);
		
		JTextPane txtPane = new JTextPane();
		String text = "";
		text += this.toString();
		if (this.fitnessResult != null) {
			text += "Fitness: " + this.fitnessResult.toString() + "\n";
			if(!((JankenponResult) this.fitnessResult).results.isEmpty())
				text+= "Last match: "+((JankenponResult) this.fitnessResult).results.get(((JankenponResult) this.fitnessResult).results.size()-1)+"\n";
		}

		
		
		text += "Mutation: " + this.mutationInfo + "\n";
		if (this.spicies != null) {
			text += "Species: " + this.spicies.generationIndex + ","
					+ this.spicies.individualIndex + "\n";
		}
		if (this.indivCompared != null) {
			text += "Indiv compared: " + this.indivCompared.generationIndex
					+ "," + this.indivCompared.individualIndex + "\n";
		}
		txtPane.setText(text);

		JTextPane modelString = new JTextPane();
		modelString.setText(this.modelString);
		tabbedPane.addTab("NEAT", txtPane);
		tabbedPane.addTab("NATH", modelString);
	
		return info;
	}
	
	public void evaluateFitness() {
		boolean debug = false;
		if(debug){
		this.fitnessResult = new EloResult();
		for(int i=0;i<this.lastMatches.size();i++){
			try {
				new JankenponELOMatch(this, (JankenponIndividual) this.lastMatches.get(i)).call();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		}
	}
	
	@Override
	public String mutateParameter() {
		StringBuilder info = new StringBuilder();
		info.append("Mutate Parameters:\n Nodes: \n");
		if (config.seqC.optimizeMechanism == RunConfig.SEQC_OPTIMIZE_YES) {
			info.append(" Concentrations:\n");
			for (Node node : nodeGenes) {
				if (rand.nextDouble() < Constants.probGeneMutation) {
					double oldParameter = node.concentration;
					node.concentration = mutateParam(node.concentration,
							config.seqC.minValue, config.seqC.maxValue);
					info.append(node.name + ": " + oldParameter + "->"
							+ node.concentration + "\n");
				}
			}
		}
		if (config.seqK.optimizeMechanism == RunConfig.SEQK_OPTIMIZE_YES) {
			info.append("\n K params:\n");
			for (Node node : nodeGenes) {
				if (node.type == Node.SIMPLE_SEQUENCE && !node.name.equals(ConstantsJankenpon.JUDGESTRING)&& !node.name.equals(ConstantsJankenpon.JANSTRING)&& !node.name.equals(ConstantsJankenpon.KENSTRING)&& !node.name.equals(ConstantsJankenpon.PONSTRING)) {
					if (rand.nextDouble() < Constants.probGeneMutation) {
						double oldParameter = node.parameter;
						node.parameter = mutateParam(node.parameter,
								config.seqK.minValue, config.seqK.maxValue);

						info.append(node.name + ": " + oldParameter + "->"
								+ node.parameter + "\n");
					}
				}
			}
		}

		if (config.template.optimizeMechanism == RunConfig.TEMPLATE_OPTIMIZE_YES) {
			info.append("\n Connections: \n");
			for (int i = connectionGenes.size() - 1; i >= 0; i--) {
				Connection connection = connectionGenes.get(i);
				boolean subZero = false;
				if (connection.enabled
						&& rand.nextDouble() < Constants.probGeneMutation) {
					double oldParameter = connection.parameter;
					boolean disabled = false;
					if (rand.nextDouble() < MyUtil
							.probDisableConnection(connection.parameter)
							&& getNEnabledConnections() > 1) {
						connection.enabled = false;
						disabled = true;
					} else {
						connection.parameter = mutateParam(
								connection.parameter, config.template.minValue,
								config.template.maxValue);
					}
					if (connection.parameter < 0) {
						subZero = true;
						connection.enabled = false;
						connection.parameter = -connection.parameter;
						if (connection.to.type == Node.INHIBITING_SEQUENCE) {
							Connection inhibitedConnection = getInhibitedConnection(connection.to);
							if (inhibitedConnection != null) {
								addConnection(connection.from,
										inhibitedConnection.to, true,
										connection.parameter);
							}
						} else {
							mutateAddInhibition(connection.from, connection.to,
									config.template.defaultValue);
						}
					}
					if (disabled) {
						info.append(connection.from.name + connection.to.name
								+ ": Disabled");
					} else {
						info.append(connection.from.name + connection.to.name
								+ ": " + oldParameter + "->"
								+ (subZero ? "(-)" : "") + connection.parameter
								+ "\n");
					}
				}
			}
		}
		if (config.polActivity.optimizeMechanism == RunConfig.POL_OPTIMIZE_YES) {
			double oldParameter = pol;
			pol = mutateParam(pol, config.polActivity.minValue, config.polActivity.maxValue);
			info.append("\nK pol:\n" + oldParameter + "->" + pol + "\n");
		}
		if (config.exoActivity.optimizeMechanism == RunConfig.EXO_OPTIMIZE_YES) {
			double oldParameter = exo;
			exo = mutateParam(exo, config.exoActivity.minValue, config.exoActivity.maxValue);
			info.append("\nK pol:\n" + oldParameter + "->" + exo + "\n");
		}
		if (config.nickActivity.optimizeMechanism == RunConfig.NICK_OPTIMIZE_YES) {
			double oldParameter = nick;
			nick = mutateParam(nick, config.nickActivity.minValue, config.nickActivity.maxValue);
			info.append("\nK nick:\n" + oldParameter + "->" + nick + "\n");
		}
		mutationInfo = info.toString();
		return mutationInfo;
	}
	
	@Override
	public String mutateAddNode() {
		if (rand.nextDouble() < 1.0f / (getNSimpleSequences() + 1)) {
			// new mutation rule
			if (rand.nextBoolean()) {
				// new node connect to a simple sequence node
				Node to = null;
				while (to == null || to.type == Node.INHIBITING_SEQUENCE || to.name.equals(ConstantsJankenpon.JUDGESTRING) || to.name.startsWith(ConstantsJankenpon.contestantPrefix)) {
					int index = rand.nextInt(nodeGenes.size());
					to = nodeGenes.get(index);
				}
				int i = nodeNames.size() + 99;
				char c = (char) i;
				String nodeName = Character.toString(c);
				String nodeOrigins = "null->" + to.name;
				if (nodeNames.get(nodeOrigins) == null) {
					nodeNames.put(nodeOrigins, nodeName);
				} else {
					nodeName = nodeNames.get(nodeOrigins);
				}
				boolean hasNodeName = true;
				i = 0;
				while (hasNodeName) {
					if (i > 0) {
						nodeName = nodeName + String.valueOf(i);
					}
					hasNodeName = false;
					for (Node node : nodeGenes) {
						if (node.name.equals(nodeName)) {
							hasNodeName = true;
							break;
						}
					}
					i++;
				}
				Node newNode = new Node(nodeName, config.seqK.defaultValue,
						config.seqC.defaultValue, Node.SIMPLE_SEQUENCE);
				nodeGenes.add(newNode);
				addConnection(newNode, to, true, config.template.defaultValue);
				addConnection(newNode, newNode, true,
						config.template.defaultValue);
				mutationInfo = "Mutate Add Node :" + newNode.name + "->"
						+ to.name;
			} else {
				// new node connect to inhibiting sequence node
				ArrayList<Connection> possibleActivations = new ArrayList<Connection>();
				for (Connection conn : connectionGenes) {
					if (conn.to.type != Node.INHIBITING_SEQUENCE
							&& conn.enabled) {
						possibleActivations.add(conn);
					}
				}
				if (possibleActivations.size() == 0) {
					mutationInfo = "Cannot add node";
				} else {
					int index = rand.nextInt(possibleActivations.size());
					Connection connection = possibleActivations.get(index);
					String inhibitionNodeName = "I" + connection.from.name
							+ connection.to.name;
					Node inhibitionNode = getNode(inhibitionNodeName);
					if (inhibitionNode == null) {
						inhibitionNode = new Node(inhibitionNodeName,
								config.inhK.defaultValue,
								config.seqC.defaultValue,
								Node.INHIBITING_SEQUENCE);
						nodeGenes.add(inhibitionNode);
					}
					int i = nodeNames.size() + 99;
					char c = (char) i;
					String nodeName = Character.toString(c);
					String nodeOrigins = "null->" + inhibitionNode.name;
					if (nodeNames.get(nodeOrigins) == null) {
						nodeNames.put(nodeOrigins, nodeName);
					} else {
						nodeName = nodeNames.get(nodeOrigins);
					}
					boolean hasNodeName = true;
					i = 0;
					while (hasNodeName) {
						if (i > 0) {
							nodeName = nodeName + String.valueOf(i);
						}
						hasNodeName = false;
						for (Node node : nodeGenes) {
							if (node.name.equals(nodeName)) {
								hasNodeName = true;
								break;
							}
						}
						i++;
					}
					Node newNode = new Node(nodeName, config.seqK.defaultValue,
							config.seqC.defaultValue, Node.SIMPLE_SEQUENCE);
					nodeGenes.add(newNode);
					addConnection(newNode, inhibitionNode, true,
							config.template.defaultValue);
					addConnection(newNode, newNode, true,
							config.template.defaultValue);
					mutationInfo = "Mutate Add Node :" + newNode.name + "->"
							+ inhibitionNode.name;
				}
			}

		} else if (connectionGenes.size() > 0) {
			ArrayList<Connection> possibleActivations = new ArrayList<Connection>();
			for (Connection conn : connectionGenes) {
				if (conn.enabled) {
					possibleActivations.add(conn);
				}
			}
			if (possibleActivations.size() == 0) {
				mutationInfo = "Cannot Add Node";
			} else {
				Connection connection = null;

				int index = rand.nextInt(possibleActivations.size());
				connection = getCorrectConnection(possibleActivations
						.get(index));

				connection.enabled = false;
				String nodeOrigins = connection.from.name + "->"
						+ connection.to.name;
				int i = nodeNames.size() + 99;
				char c = (char) i;
				String nodeName = Character.toString(c);
				if (nodeNames.get(nodeOrigins) == null) {
					nodeNames.put(nodeOrigins, nodeName);
				} else {
					nodeName = nodeNames.get(nodeOrigins);
				}
				boolean hasNodeName = true;
				i = 0;
				while (hasNodeName) {
					if (i > 0) {
						nodeName = nodeName + String.valueOf(i);
					}
					hasNodeName = false;
					for (Node node : nodeGenes) {
						if (node.name.equals(nodeName)) {
							hasNodeName = true;
							break;
						}
					}
					i++;
				}
				Node newNode = new Node(nodeName, config.seqK.defaultValue,
						config.seqC.defaultValue, Node.SIMPLE_SEQUENCE);
				nodeGenes.add(newNode);
				// double firstParam = rand.nextDouble() * connection.parameter;
				// double secondParam = connection.parameter - firstParam;
				if (connection.to.type == Node.INHIBITING_SEQUENCE) {
					addConnection(connection.from, newNode, true,
							config.template.defaultValue);
				} else {
					addConnection(connection.from, newNode, true,
							connection.parameter);
				}
				addConnection(newNode, connection.to, true,
						connection.parameter);
				String oldNodeName = "I" + connection.from.name
						+ connection.to.name;
				for (Node node : nodeGenes) {
					if (node.name.equals(oldNodeName)) {
						node.name = "I" + connection.from.name + newNode.name;
						for (Connection conn : connectionGenes) {
							if (conn.to == node) {
								int innovation = innovations.size() + 1;
								String key = conn.from.name + "->"
										+ conn.to.name;
								if (innovations.get(key) == null) {
									innovations.put(key, innovation);
								} else {
									innovation = innovations.get(key);
								}
								conn.innovation = innovation;
							}
						}
						break;
					}
				}
				mutationInfo = "Mutate Add Node :" + connection.from.name
						+ "->" + newNode.name + "->" + connection.to.name;
			}
		}
		return mutationInfo;
	}
	
	public static void main(String[] args){
		JankenponGA ga = new JankenponGA(new TicTacToeConfiguration());
		JankenponIndividual indiv1 = (JankenponIndividual) ga.getInitialIndividual();
		//Note: those sequences are shared
		indiv1.nodeGenes.removeAll(indiv1.nodeGenes);
		indiv1.connectionGenes.removeAll(indiv1.connectionGenes);
				indiv1.nodeGenes.add(new Node(ConstantsJankenpon.JUDGESTRING, 19.775, 10, Node.SIMPLE_SEQUENCE)); // A
				indiv1.nodeGenes.add(new Node(ConstantsJankenpon.JANSTRING, 21.426, 10, Node.SIMPLE_SEQUENCE)); // a
				indiv1.nodeGenes.add(new Node(ConstantsJankenpon.KENSTRING, 14.1377, 10, Node.SIMPLE_SEQUENCE)); // b
				indiv1.nodeGenes.add(new Node(ConstantsJankenpon.PONSTRING,34.02, 10, Node.SIMPLE_SEQUENCE)); // c
				indiv1.nodeGenes.add(new Node("c1",0.5773, 10, Node.SIMPLE_SEQUENCE)); // c
				indiv1.nodeGenes.add(new Node("Ibb",0.1414, 10, Node.INHIBITING_SEQUENCE)); // c
				//For the opponent
				indiv1.nodeGenes.add(new Node(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.JANSTRING, 25, 10, Node.SIMPLE_SEQUENCE)); // a
				indiv1.nodeGenes.add(new Node(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.KENSTRING, 25, 10, Node.SIMPLE_SEQUENCE)); // b
				indiv1.nodeGenes.add(new Node(ConstantsJankenpon.contestantPrefix+ConstantsJankenpon.PONSTRING,25, 10, Node.SIMPLE_SEQUENCE)); // c
				indiv1.addConnection(indiv1.getNode(ConstantsJankenpon.KENSTRING), indiv1.getNode(ConstantsJankenpon.KENSTRING), true, 14.7018); //always play stone
				indiv1.addConnection(indiv1.getNode(ConstantsJankenpon.JUDGESTRING), indiv1.getNode("Ibb"), true, 18.7615); //always play stone
				indiv1.addConnection(indiv1.getNode(ConstantsJankenpon.JANSTRING), indiv1.getNode("c1"), true, 8.0521); //always play stone
				indiv1.addConnection(indiv1.getNode("c1"), indiv1.getNode(ConstantsJankenpon.JANSTRING), true, 3.3211); //always play stone
//			JankenponIndividual indiv2 = indiv1.clone();
//			for(int i=0; i<20; i++){
//			indiv2.mutate();
//			}
		JankenponIndividual indiv2 =  (JankenponIndividual) ga.getInitialIndividual();
		indiv2.connectionGenes.removeAll(indiv1.connectionGenes);
		indiv2.addConnection(indiv2.getNode(ConstantsJankenpon.PONSTRING), indiv2.getNode(ConstantsJankenpon.PONSTRING), true, 24.7018); //always play stone
		JankenponGame game = indiv2.competeAgainst(indiv1);
			System.out.println(indiv1.nodeGenes.size()+" "+indiv2.competeAgainst(indiv1).score+" "+indiv2.nodeGenes.size());
			JFrame display = new JFrame("test");
			display.add(indiv2.displayTimeSereis());
			display.setVisible(true);
		
		System.out.println(indiv1.nodeGenes.size()+" "+indiv1.competeAgainst(indiv1).score+" "+indiv1.nodeGenes.size());
	}

}
